//
// (C) Copyright 2009 Irantha Suwandarathna (irantha@gmail.com)
// All rights reserved.
//

/* Copyright (c) 1995-2000, The Hypersonic SQL Group.
 * All rights reserved.
 *
 * Redistribution and use _in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer.
 *
 * Redistributions _in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer _in the documentation
 * and/or other materials provided with the distribution.
 *
 * Neither the name of the Hypersonic SQL Group nor the names of its
 * contributors may be used to endorse or promote products derived from this
 * software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE HYPERSONIC SQL GROUP,
 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * on behalf of the Hypersonic SQL Group.
 *
 *
 * For work added by the HSQL Development Group:
 *
 * Copyright (c) 2001-2008, The HSQL Development Group
 * All rights reserved.
 *
 * Redistribution and use _in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer.
 *
 * Redistributions _in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer _in the documentation
 * and/or other materials provided with the distribution.
 *
 * Neither the name of the HSQL Development Group nor the names of its
 * contributors may be used to endorse or promote products derived from this
 * software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL HSQL DEVELOPMENT GROUP, HSQLDB.ORG,
 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */


using System;
using EffiProz.Core.Lib;
using EffiProz.Core.RowIO;
using EffiProz.Core.Persist;
using EffiProz.Core.Errors;


namespace EffiProz.Core.Indexes
{


    // fredt@users 20020221 - patch 513005 by sqlbob@users (RMP)
    // fredt@users 20020920 - path 1.7.1 - refactoring to cut mamory footprint
    // fredt@users 20021205 - path 1.7.2 - enhancements

    /**
     *  Cached table Node implementation.<p>
     *  Only integral references to left, right and parent nodes _in the AVL tree
     *  are held and used as pointers data.<p>
     *
     *  iId is a reference to the Index object that contains this node.<br>
     *  This fields can be eliminated _in the future, by changing the
     *  method signatures to take a Index parameter from Index.java (fredt@users)
     *
     *  New class derived from the Hypersonic code
     *
     * @author Thomas Mueller (Hypersonic SQL Group)
     * @version    1.7.2
     * @since Hypersonic SQL
     */
    class NodeAVLDisk : NodeAVL
    {
        protected RowAVLDisk row;
        //
        protected NodeAVLDisk nLeft;
        protected NodeAVLDisk nRight;
        protected NodeAVLDisk nParent;



        public int iData;
        private int iLeft = NO_POS;
        private int iRight = NO_POS;
        private int iParent = NO_POS;
        private int iId;    // id of Index object for this Node
        public const int SIZE_IN_BYTE = 4 * 4;

        public NodeAVLDisk(RowAVLDisk r, RowInputInterface _in,
                 int id)
        {

            iId = id;
            row = r;
            iData = r.getPos();

            iBalance = _in.readInt();
            iLeft = _in.readInt();

            if (iLeft <= 0)
            {
                iLeft = NO_POS;
            }

            iRight = _in.readInt();

            if (iRight <= 0)
            {
                iRight = NO_POS;
            }

            iParent = _in.readInt();

            if (iParent <= 0)
            {
                iParent = NO_POS;
            }

        }

        public NodeAVLDisk(RowAVLDisk r, int id)
        {
            iId = id;
            row = r;
            iData = r.getPos();
        }

        public override void delete()
        {
            iLeft = NO_POS;
            iRight = NO_POS;
            iParent = NO_POS;
            nLeft = null;
            nRight = null;
            nParent = null;
            iBalance = 0;

            row.setNodesChanged();
        }


        public override bool isInMemory()
        {
            return row.isInMemory();
        }

        public override bool isMemory()
        {
            return false;
        }

        public override int getPos()
        {
            return iData;
        }

        public override Row getRow(PersistentStore store)
        {
            if (!row.isInMemory())
            {
                return (RowAVLDisk)store.get(this.row, false);
            }
            else
            {
                row.updateAccessCount(store.getAccessCount());
            }

            return row;
        }

        private NodeAVLDisk findNode(PersistentStore store, int pos)
        {
            NodeAVLDisk ret = null;
            RowAVLDisk r = (RowAVLDisk)store.get(pos, false);

            if (r != null)
            {
                ret = (NodeAVLDisk)r.getNode(iId);
            }

            return ret;
        }

        public override Object[] getData(PersistentStore store)
        {
            return row.getData();
        }

        public override bool isLeft(NodeAVL node)
        {

            if (node == null)
            {
                return iLeft == NO_POS;
            }

            return iLeft == ((NodeAVLDisk)node).getPos();
        }

        public override bool isRight(NodeAVL node)
        {

            if (node == null)
            {
                return iRight == NO_POS;
            }

            return iRight == ((NodeAVLDisk)node).getPos();
        }

        public override NodeAVL getLeft(PersistentStore store)
        {
            NodeAVLDisk node = this;
            RowAVLDisk row = this.row;

            if (!row.isInMemory())
            {
                row = (RowAVLDisk)store.get(this.row, false);
                node = (NodeAVLDisk)row.getNode(iId);
            }

            if (node.iLeft == NO_POS)
            {
                return null;
            }

            if (node.nLeft == null || !node.nLeft.isInMemory())
            {
                node.nLeft = findNode(store, node.iLeft);
                node.nLeft.nParent = node;
            }

            return node.nLeft;
        }

        public override NodeAVL getRight(PersistentStore store)
        {
            NodeAVLDisk node = this;
            RowAVLDisk row = this.row;

            if (!row.isInMemory())
            {
                row = (RowAVLDisk)store.get(this.row, false);
                node = (NodeAVLDisk)row.getNode(iId);
            }

            if (node.iRight == NO_POS)
            {
                return null;
            }

            if (node.nRight == null || !node.nRight.isInMemory())
            {
                node.nRight = findNode(store, node.iRight);
                node.nRight.nParent = node;
            }

            return node.nRight;

        }

        public override NodeAVL getParent(PersistentStore store)
        {
            NodeAVLDisk node = this;
            RowAVLDisk row = this.row;

            if (!row.isInMemory())
            {
                row = (RowAVLDisk)store.get(this.row, false);
                node = (NodeAVLDisk)row.getNode(iId);
            }

            if (node.iParent == NO_POS)
            {
                return null;
            }

            if (node.nParent == null || !node.nParent.isInMemory())
            {
                node.nParent = findNode(store, iParent);
            }

            return node.nParent;

        }

        public override int getBalance(PersistentStore store)
        {

            NodeAVLDisk node = this;
            RowAVLDisk row = this.row;

            if (!row.isInMemory())
            {
                row = (RowAVLDisk)store.get(this.row, false);
                node = (NodeAVLDisk)row.getNode(iId);
            }

            return node.iBalance;
        }

        public override bool isRoot(PersistentStore store)
        {
            NodeAVLDisk node = this;
            RowAVLDisk row = this.row;

            if (!row.isInMemory())
            {
                row = (RowAVLDisk)store.get(this.row, false);
                node = (NodeAVLDisk)row.getNode(iId);
            }

            return node.iParent == NO_POS;

        }

        public override bool isFromLeft(PersistentStore store)
        {
            NodeAVLDisk node = this;
            RowAVLDisk row = this.row;

            if (!row.isInMemory())
            {
                row = (RowAVLDisk)store.get(this.row, false);
                node = (NodeAVLDisk)row.getNode(iId);
            }

            if (node.iParent == NO_POS)
            {
                return true;
            }

            if (node.nParent == null || !node.nParent.isInMemory())
            {
                node.nParent = findNode(store, iParent);
            }

            return getPos() == node.nParent.iLeft;

        }


        public override NodeAVL setParent(PersistentStore store, NodeAVL n)
        {

            NodeAVLDisk node = this;
            RowAVLDisk row = this.row;

            if (!row.keepInMemory(true))
            {
                row = (RowAVLDisk)store.get(this.row, true);
                node = (NodeAVLDisk)row.getNode(iId);
            }

            if (!row.isInMemory())
            {
                throw Error.runtimeError(ErrorCode.U_S0500, "NodeAVLDisk");
            }

            row.setNodesChanged();

            node.iParent = n == null ? NO_POS
                                     : n.getPos();

            if (n != null && !n.isInMemory())
            {
                n = findNode(store, n.getPos());
            }

            node.nParent = (NodeAVLDisk)n;

            row.keepInMemory(false);

            return node;
        }

        public override NodeAVL setBalance(PersistentStore store, int b)
        {

            NodeAVLDisk node = this;
            RowAVLDisk row = this.row;

            if (!row.keepInMemory(true))
            {
                row = (RowAVLDisk)store.get(this.row, true);
                node = (NodeAVLDisk)row.getNode(iId);
            }

            if (!row.isInMemory())
            {
                throw Error.runtimeError(ErrorCode.U_S0500, "NodeAVLDisk");
            }

            row.setNodesChanged();

            node.iBalance = b;

            row.keepInMemory(false);

            return node;
        }

        public override NodeAVL setLeft(PersistentStore store, NodeAVL n)
        {
            NodeAVLDisk node = this;
            RowAVLDisk row = this.row;

            if (!row.keepInMemory(true))
            {
                row = (RowAVLDisk)store.get(this.row, true);
                node = (NodeAVLDisk)row.getNode(iId);
            }

            if (!row.isInMemory())
            {
                throw Error.runtimeError(ErrorCode.U_S0500, "NodeAVLDisk");
            }

            row.setNodesChanged();

            node.iLeft = n == null ? NO_POS
                                   : n.getPos();

            if (n != null && !n.isInMemory())
            {
                n = findNode(store, n.getPos());
            }

            node.nLeft = (NodeAVLDisk)n;

            row.keepInMemory(false);

            return node;
        }

        public override NodeAVL setRight(PersistentStore store, NodeAVL n)
        {
            NodeAVLDisk node = this;
            RowAVLDisk row = this.row;

            if (!row.keepInMemory(true))
            {
                row = (RowAVLDisk)store.get(this.row, true);
                node = (NodeAVLDisk)row.getNode(iId);
            }

            if (!row.isInMemory())
            {
                throw Error.runtimeError(ErrorCode.U_S0500, "NodeAVLDisk");
            }

            row.setNodesChanged();

            node.iRight = n == null ? NO_POS
                                    : n.getPos();

            if (n != null && !n.isInMemory())
            {
                n = findNode(store, n.getPos());
            }

            node.nRight = (NodeAVLDisk)n;

            row.keepInMemory(false);

            return node;
        }

        public override NodeAVL set(PersistentStore store, bool isLeft, NodeAVL n)
        {

            NodeAVL x;

            if (isLeft)
            {
                x = setLeft(store, n);
            }
            else
            {
                x = setRight(store, n);
            }

            if (n != null)
            {
                n.setParent(store, this);
            }

            return x;
        }

        public override void replace(PersistentStore store, Index index, NodeAVL n)
        {

            if (isRoot(store))
            {
                if (n != null)
                {
                    n = n.setParent(store, null);
                }

                store.setAccessor(index, n);
            }
            else
            {
                bool _isFromLeft = isFromLeft(store);

                getParent(store).set(store, _isFromLeft, n);
            }
        }

        public override bool Equals(NodeAVL n)
        {

            return this == n
                   || (n != null && getPos() == ((NodeAVLDisk)n).getPos());
        }




        public override int getRealSize(RowOutputInterface _out)
        {
            return NodeAVLDisk.SIZE_IN_BYTE;
        }

        public override void setInMemory(bool _in)
        {

            if (!_in)
            {
                if (nLeft != null)
                {
                    nLeft.nParent = null;
                }

                if (nRight != null)
                {
                    nRight.nParent = null;
                }

                if (nParent != null)
                {
                    if (iData == nParent.iLeft)
                    {
                        nParent.nLeft = null;
                    }
                    else
                    {
                        nParent.nRight = null;
                    }
                }

                nLeft = nRight = nParent = null;
            }
        }

        public override void write(RowOutputInterface _out)
        {
            _out.writeInt(iBalance);
            _out.writeInt((iLeft == NO_POS) ? 0
                                               : iLeft);
            _out.writeInt((iRight == NO_POS) ? 0
                                                : iRight);
            _out.writeInt((iParent == NO_POS) ? 0
                                                 : iParent);
        }


        public void writeTranslate(RowOutputInterface _out, IntLookup lookup)
        {

            _out.writeInt(iBalance);
            writeTranslatePointer(iLeft, _out, lookup);
            writeTranslatePointer(iRight, _out, lookup);
            writeTranslatePointer(iParent, _out, lookup);
        }

        private void writeTranslatePointer(int pointer, RowOutputInterface _out,
                                           IntLookup lookup)
        {

            int newPointer = 0;

            if (pointer != NodeAVL.NO_POS)
            {
                newPointer = lookup.lookupFirstEqual(pointer);
            }

            _out.writeInt(newPointer);
        }
    }
}
